﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using DNADataHiding.Model.Shared;
using DNADataHiding.Exceptions;

namespace DNADataHiding.Model.Encryption
{
    class SubstitutionEncryptor : IEncryptor
    {
        private List<int> _p;
        
        public SubstitutionEncryptor()
        {
        }
        
        //PRE: el largo maximo del mensaje es baseSequence/8  
        public DNASequence Encrypt(string message, DNASequence baseSequence)
        {

           //get the binary form of message and validate that its binaryform its no greater than baseSequence lenght
           string binaryMessage = EncryptionUtils.GetBinaryMessage(message);

            /*Just for test
            string binaryMessage = "0111010";
            _p = new List<int>();
            _p.Add(1);
            _p.Add(2);
            _p.Add(4);
            _p.Add(9);
            _p.Add(11);
            _p.Add(12);
            _p.Add(14);*/
            
            if (binaryMessage.Length > baseSequence.Length())
            {
                throw new ModelException("El mensaje en su forma binaria es mas grande que la sequencia de DNA");
            }

            //generates 'sequencelenght´  random values that goes from 1 to sequencelenght, and sort them
            ISet<int> randomSet = new HashSet<int>();
            Random randomP = new Random();
            while (randomSet.Count < binaryMessage.Length)
            {
                randomSet.Add(randomP.Next(0, baseSequence.Length()));
            }
            _p = randomSet.ToList();
            _p.Sort();

            StringBuilder sequenceBuilder = new StringBuilder();

            for (int i = 0; i < baseSequence.Length(); i++) 
            { 
                char nucleotide = baseSequence.GetNucleotideAt(i);
                if (_p.Contains(i))
                {
                    int index = _p.IndexOf(i);
                    if (binaryMessage.ElementAt(index).Equals('1'))
                    {
                        sequenceBuilder.Append(ComplementRule.GetComplement(nucleotide));
                    }
                    else
                    {
                        sequenceBuilder.Append(nucleotide);
                    }
                }
                else
                {
                    sequenceBuilder.Append(ComplementRule.GetComplement(ComplementRule.GetComplement(nucleotide)));
                }
            }

            return new DNASequence(sequenceBuilder.ToString());
        }

        public string Decrypt(IList<DNASequence> sequences, DNASequence baseSequence)
        {
            if (sequences.Count != 1)
            {
                throw new ModelException("Hay menos o mas de un mensaje secreto a desencriptar");
            }

            DNASequence encryptedSequence = sequences.ElementAt(0);
            StringBuilder messageBuilder = new StringBuilder();
            int j = 0;
            
            for (int i = 0; i < encryptedSequence.Length() - 1; i++) 
            {
                if (encryptedSequence.GetNucleotideAt(i).Equals(baseSequence.GetNucleotideAt(i))) {
                    messageBuilder.Append('0');
                }
                else if (encryptedSequence.GetNucleotideAt(i).Equals(
                    ComplementRule.GetComplement(baseSequence.GetNucleotideAt(i))))
                {
                    messageBuilder.Append('1');
                }
            }

            return EncryptionUtils.GetMessageFromBinary(messageBuilder.ToString());
        }
        
        public bool NeedsRandomListOfSequences()
        {
            return false;
        }
        
        public int ExtraEncryptionOutput
        {
            get
            {
                return 0;
            }
        }
    }
}
